<!-- 模板代码 -->
<template>
    <!-- 行布局 -->
    <a-row>
        <!-- 列布局，第一列占整个网页的宽度比为 16/24 -->
        <a-col :span="8">

            <!-- 第一行：正反向提示词 -->
            <a-row class="margin">
                <!-- 第一个文本域组件，用于输入sd的正向提示词 -->
                <a-textarea placeholder="正向prompt" allow-clear :rows="5" show-count :maxlength="2000" class="textarea"
                    v-model:value="txt2img_payload.prompt" />
                <!-- 第二个文本域组件，用于输入sd的反向提示词 -->
                <a-textarea placeholder="反向prompt" allow-clear :rows="5" show-count :maxlength="2000" class="textarea"
                    v-model:value="txt2img_payload.negative_prompt" />
            </a-row>

            <!-- 第二行：采样方法 -->
            <a-row class="margin">
                <!-- 小标题组件tag -->
                <a-tag color="blue" class="tag">采样方法</a-tag>
                <a-col :span="12">
                <!-- 下拉框组件select
                        placeholder：占位符提示
                                    更多select的熟悉感可以参考ant-design-vue官方文档  -->
                    <a-select placeholder="请选择采样方法" :allowClear="true" class="select" v-model:value="txt2img_payload.sampler_name"
                        :options="txt2ImgSamplerOptions">
                    </a-select>
                </a-col>
            </a-row>

            <!-- 第三行：采样步数 -->
            <a-row class="margin">
                <a-tag class="tag" color="orange">采样步数</a-tag>
                <a-col :span="6">
                <!-- 
                        min代表最小值
                        max代表最大值
                                                                                                         -->
                    <a-slider :min="1" :max="150" v-model:value="txt2img_payload.steps" />
                </a-col>
                <a-col :span="2">
                    <a-input-number :min="1" :max="150" v-model:value="txt2img_payload.steps" />
                </a-col>
            </a-row>

            <!-- 第四行：宽 -->
            <a-row class="margin">
                <a-tag class="tag_1" color="green">宽</a-tag>
                <a-col :span="6">
                    <a-slider :min="64" :max="2048" v-model:value="txt2img_payload.width" />
                </a-col>
                <a-col :span="2">
                    <a-input-number :min="64" :max="2048" v-model:value="txt2img_payload.width" />
                </a-col>
            </a-row>

            <!-- 第五行：高 -->
            <a-row class="margin">
                <a-tag class="tag_1" color="green">高</a-tag>
                <a-col :span="6">
                    <a-slider :min="64" :max="2048" v-model:value="txt2img_payload.height" />
                </a-col>
                <a-col :span="2">
                    <a-input-number :min="64" :max="2048" v-model:value="txt2img_payload.height" />
                </a-col>
            </a-row>

            <!-- 第六行：提示词相关性 -->
            <a-row class="margin row_tip">
                <a-tag class="tag" color="cyan">提示词相关性</a-tag>
                <a-col :span="5">
                    <a-slider :min="1" :max="30" v-model:value="txt2img_payload.cfg_scale" />
                </a-col>
                <a-col :span="2">
                    <a-input-number :min="1" :max="30" v-model:value="txt2img_payload.cfg_scale" />
                </a-col>
            </a-row>

            <!-- 第七行：每批数量 -->
            <a-row class="margin">
                <a-tag class="tag" color="pink">每批数量</a-tag>
                <a-col :span="6">
                    <a-slider :min="1" :max="8" v-model:value="txt2img_payload.batch_size" />
                </a-col>
                <a-col :span="2">
                    <a-input-number :min="1" :max="8" v-model:value="txt2img_payload.batch_size" />
                </a-col>
            </a-row>

            <!-- 第八行：种子 -->
            <a-row class="margin">
                <a-tag class="tag_1" color="purple">种子</a-tag>
                <a-col :span="12">
                    <a-input-number class="seal" :min="-1" v-model:value="txt2img_payload.seed" />
                </a-col>
            </a-row>

            <!-- 第九行：文生图按钮 -->
            <a-row class="margin">
                <a-col :span="16">
                    <a-button class="button_text2img" type="primary" shape="round" size="large" @click="txt2Img">
                        文生图
                    </a-button>
                </a-col>
            </a-row>
        </a-col>

        <!-- 列布局，第二列占整个网页的宽度比为 8/24 -->
        <a-col :span="8">
            <a-collapse :bordered="false" class="margin collapse">
                <template #expandIcon="{ isActive }">
                    <caret-right-outlined :rotate="isActive ? 90 : 0" />
                </template>
                <a-collapse-panel header="ControlNet" :style="collapsePanelStyle">
                    <!-- 控制ControlNet开关 -->
                    <a-row class="margin">
                        <a-tag class="tag" color="blue">ControlNet</a-tag>
                        <a-switch class="checked" v-model:checked="controlNetEnable" />
                    </a-row>
                    <a-row class="margin">
                        <!-- 图片上传组件 -->
                        <a-upload list-type="picture" :max-count="1" v-model:file-list="imgFileList"
                            :beforeUpload="beforeUploadHandle" @change="handleChange">
                            <a-button>
                                <upload-outlined></upload-outlined>
                                上传图片
                            </a-button>
                        </a-upload>
                    </a-row>
                    <!--  Low VRAM & Pixel Perfect -->
                    <a-row class="margin">
                        <a-checkbox v-model:checked="controlNet_payload.lowvram">Low VRAM</a-checkbox>
                        <a-checkbox v-model:checked="controlNet_payload.pixel_perfect">Pixel Perfect</a-checkbox>
                    </a-row>
                    <!-- 预处理/Preprocessor -->
                    <a-row class="margin">
                        <a-tag class="tag" color="pink">Preprocessor</a-tag>
                        <a-select placeholder="请选择预处理器" :allowClear="true" class="select" v-model:value="controlNet_payload.module" :options="controleNetModuleSelect">
                        </a-select>
                    </a-row>
                    <!-- 模型/Model -->
                    <a-row class="margin">
                        <a-tag class="tag" color="green">Model</a-tag>
                        <a-select placeholder="请选择模型" :allowClear="true" class="select" v-model:value="controlNet_payload.model" :options="controleNetModelSelect">
                        </a-select>
                    </a-row>
                    <!-- 控制权重/Control Weight -->
                    <a-row class="margin">
                        <a-tag class="tag" color="cyan">Control Weight</a-tag>
                        <a-col :span="14">
                            <a-slider v-model:value="controlNet_payload.weight" :min="0" :max="2" :step="0.05" />
                        </a-col>
                        <a-col :span="2">
                            <a-input-number v-model:value="controlNet_payload.weight" :min="0" :max="2" :step="0.05"
                                style="margin-left: 6px" />
                        </a-col>
                    </a-row>
                    <!-- 启动控制的步数/Starting Control Step -->
                    <a-row class="margin">
                        <a-tag class="tag" color="cyan">Starting Control Step</a-tag>
                        <a-col :span="13">
                            <a-slider v-model:value="controlNet_payload.guidance_start" :min="0" :max="1" :step="0.01" />
                        </a-col>
                        <a-col :span="2">
                            <a-input-number v-model:value="controlNet_payload.guidance_start" :min="0" :max="1" :step="0.01"
                                style="margin-left: 6px" />
                        </a-col>
                    </a-row>
                    <!-- 结束控制的步数/Ending Control Step -->
                    <a-row class="margin">
                        <a-tag class="tag" color="cyan">Ending Control Step</a-tag>
                        <a-col :span="13">
                            <a-slider v-model:value="controlNet_payload.guidance_end" :min="0" :max="1" :step="0.01" />
                        </a-col>
                        <a-col :span="2">
                            <a-input-number v-model:value="controlNet_payload.guidance_end" :min="0" :max="1" :step="0.01"
                                style="margin-left: 6px" />
                        </a-col>
                    </a-row>
                    <!-- 控制模式/Control Mode -->
                    <a-row class="margin">
                        <a-tag class="tag" color="orange">Control Mode</a-tag>
                        <a-select class="select" v-model:value="controlNet_payload.control_mode"
                            :options="controlModeSelect">
                        </a-select>
                    </a-row>
                    <!-- 缩放模式/Resize Mode -->
                    <a-row class="margin">
                        <a-tag class="tag" color="purple">Resize Mode</a-tag>
                        <a-select class="select" v-model:value="controlNet_payload.resize_mode" :options="resizeModeSelect">
                        </a-select>
                    </a-row>
                </a-collapse-panel>
            </a-collapse>
        </a-col>


        <!-- 列布局，第三列占整个网页的宽度比为 8/24 -->
        <a-col :span="8">
            <swiper :style="{
                '--swiper-navigation-color': '#fff',
                '--swiper-pagination-color': '#fff',
            }" :autoplay="{ delay: 2500, disableOnInteraction: false }" :loop="true" :spaceBetween="10"
                :navigation="true" :thumbs="{ swiper: thumbsSwiper }" :modules="modules" class="mySwiper2">
                <!-- 循环渲染变量 txt2img_imgs 中的图片 -->
                <template v-for="i of txt2img_imgs" :key="i">
                    <swiper-slide><img :src="i" /></swiper-slide>
                </template>
            </swiper>
            <swiper @swiper="setThumbsSwiper" :loop="true" :spaceBetween="10" :slidesPerView="4" :freeMode="true"
                :watchSlidesProgress="true" :modules="modules" class="mySwiper">
                <!-- 循环渲染变量 txt2img_imgs 中的图片 -->
                <template v-for="i of txt2img_imgs" :key="i">
                    <swiper-slide><img :src="i" /></swiper-slide>
                </template>
            </swiper>
        </a-col>
    </a-row>
</template>

<!-- 脚本代码 -->
<script lang="ts" setup>
import axios from 'axios';
import { ref, onMounted } from 'vue'
import { CaretRightOutlined, UploadOutlined } from '@ant-design/icons-vue';
import { SelectProps, UploadProps, UploadChangeParam, message } from 'ant-design-vue';

import { Txt2ImgReq, Txt2ImgRes, Txt2ImgSamplers, ControlNetReq, txt2imgReqStorage, txt2imgResultStorage } from '../model/Txt2ImgModel'
import { ControleNetModuleRes, ControlNetModelRes } from '../model/ControlNetModel'


// 轮播图组件
import { Swiper, SwiperSlide } from 'swiper/vue'
import { FreeMode, Navigation, Thumbs, Autoplay } from 'swiper'
import 'swiper/css';
import "swiper/css/free-mode"
import "swiper/css/navigation"
import "swiper/css/thumbs"
import '../assets/main.css'

// 将所有组件的输入绑定到变量 txt2img_payload，该变量将作为文生图api的输入参数
const txt2img_payload = ref<Txt2ImgReq>({
    prompt: "",
    negative_prompt: "",
    width: 512,
    height: 512,
    steps: 20,
    cfg_scale: 7,
    sampler_name: 'Euler a',
    batch_size: 1,
    seed: -1,
    save_images: true,
    alwayson_scripts: {}
})


// ControlNet组件参数将绑定到该变量
const controlNet_payload = ref<ControlNetReq>({
    input_image: "",
    module: "",
    model: "",
    lowvram: false,
    pixel_perfect: false,
    weight: 1,
    guidance_start: 0,
    guidance_end: 1,
    control_mode: 0,
    resize_mode: 1,
    processor_res: 64,
    threshold_a: 64,
    threshold_b: 64
})


// 文生图api调用结果封装
const txt2imgRes = ref<Txt2ImgRes>()
// 文生图调用结果图片
const txt2img_imgs = ref<string[]>([])


// 采样方法下拉选择框的值
const txt2ImgSamplerOptions = ref<SelectProps['options']>([])
// 接收采样方法api调用的结果
const txt2ImgSamplerRes = ref<Txt2ImgSamplers[]>()


// 轮播图组件
const modules = [FreeMode, Navigation, Thumbs, Autoplay];
let thumbsSwiper = ref();
const setThumbsSwiper = (swiper: any) => {
    thumbsSwiper = swiper;
}


// 折叠面模样式
const collapsePanelStyle = 'background: #f7f7f7;border-radius: 4px;margin-bottom: 24px;border: 0;overflow: hidden';


// 上传组件相关
const imgFileList = ref<UploadProps['fileList']>([]);
const handleChange = ({ file, fileList }: UploadChangeParam) => {
    imgFileList.value = fileList
    console.log("ControlNet 上传的图片：", file, fileList);
};
const beforeUploadHandle = () => {
    return false
};


// ControlNet开关
const controlNetEnable = ref<boolean>(false)
// ControlNet 的 Preprocessor 下拉框中的值
const controleNetModuleSelect = ref<SelectProps['options']>([])
// ControlNet 的 Model 下拉框中的值
const controleNetModelSelect = ref<SelectProps['options']>([])
// 接收获取 ControlNet Preprocessor 的api的响应结果
const controlNetModuleRes = ref<ControleNetModuleRes>()
// 接收获取 ControlNet Model 的api的响应结果
const controlNetModelRes = ref<ControlNetModelRes>()
// ControlNet 的 Control Mode 下拉框的值
const controlModeSelect = ref<SelectProps['options']>([
    { value: 0, label: 'Balanced' },
    { value: 1, label: 'My prompt is more important' },
    { value: 2, label: 'ControlNet is more important' }
])
// ControlNet 的 Resize Mode 下拉框的值
const resizeModeSelect = ref<SelectProps['options']>([
    { value: 0, label: 'Just Resize' },
    { value: 1, label: 'Scale to Fit (Inner Fit)' },
    { value: 2, label: 'Envelope (Outer Fit)' }
])


// 定义文生图请求函数
async function txt2Img() {
    // 每次请求之前清空浏览器的 localStorage 中的缓存
    txt2imgReqStorage.value = null
    txt2imgResultStorage.value = null

    // 清空轮播图
    txt2img_imgs.value = []

    // 如果启用了 ControleNet 则将 ControlNet 相关组件中的参数填充到 文生图请求参数中
    if (controlNetEnable.value === true) {
        console.log(imgFileList.value)
        if (imgFileList.value?.length === 0) {
            message.error("ControlNet已启用，请上传图片")
            return
        }
        if (controlNet_payload.value.module === '') {
            message.error("ControlNet已启用，请选择预处理器")
            return
        }
        if (controlNet_payload.value.model === '') {
            message.error("ControlNet已启用，请选择模型")
            return
        }

        imgFileList.value?.forEach((value, index) => {
            if (index === 0) {
                controlNet_payload.value.input_image = value.thumbUrl || ''
            }
        })
        txt2img_payload.value.alwayson_scripts = {
            controlnet: {
             args: [controlNet_payload.value]
            }
        }
    } else {
        txt2img_payload.value.alwayson_scripts = {}
    }

    // 打印请求参数
    console.log("文生图请求参数：", txt2img_payload.value)
    // 将请求参数保存在浏览器的 localStorage 中
    txt2imgReqStorage.value = txt2img_payload.value;
    // 文生图api调用
    const resp = await axios.post('/sdapi/v1/txt2img', txt2img_payload.value)
    // 打印请求结果
    console.log("文生图响应结果：", resp)
    if (resp.data) {
        message.success('请求成功！')
        // 提取请求结果中的图片
        txt2imgRes.value = resp.data
        // 定义一个临时数组，该数组将赋值给 txt2imgResultStorage
        let tempArr = ref<string[]>([])
        txt2imgRes.value?.images.forEach((value: string, index: number) => {
            txt2img_imgs.value.push('data:image/png;base64,' + value)
            // 临时数组中只存放2张图片，防止赋值给 txt2imgResultStorage 后超过5M（localStorage最大支持5M），抛出异常
            if (index < 2) {
                tempArr.value.push('data:image/png;base64,' + value)
            }
        })
        //将提取的请求结果中的图片保存在浏览器的 localStorage 中，如果叠加显示每次生成的图片，请把以下三行代码的注释去掉即可
        // if (tempArr.value.length > 0) {
        //     txt2imgResultStorage.value = tempArr.value
        // }
    } else {
        message.error('请求失败！')
    }
}

// 获取文生图的采样方法
async function txt2ImgSampler() {
    const resp = await axios.get("/sdapi/v1/samplers")
    console.log("采样方法：", resp)
    txt2ImgSamplerRes.value = resp.data
    txt2ImgSamplerRes.value?.forEach((sampler: Txt2ImgSamplers) => {
        txt2ImgSamplerOptions.value?.push({
            value: sampler.name,
            label: sampler.name
        })
    })
}

// 获取 ControlNet 的 Preprocessor 的值
async function controlNetPreProcessors() {
    const resp = await axios.get('/controlnet/module_list')
    console.log("ControlNet-Preprocessor：", resp)
    controlNetModuleRes.value = resp.data
    controlNetModuleRes.value?.module_list.forEach((module: string) => {
        controleNetModuleSelect.value?.push({ value: module, label: module })
    })
}

// 获取 ControlNet 的 Model 的值
async function controlNetModels() {
    const resp = await axios.get('/controlnet/model_list')
    console.log("ControlNet-Model：", resp)
    controlNetModelRes.value = resp.data
    controlNetModelRes.value?.model_list.forEach((model: string) => {
        controleNetModelSelect.value?.push({ value: model, label: model })
    })
}

// 该函数将在页面每次加载后进行调用
onMounted(() => {
    // 页面加载后获取保存在浏览器的 localStorage 中的数据去填充组件的输入
    if (Object.keys(txt2imgReqStorage.value).length !== 0) {
        txt2img_payload.value = txt2imgReqStorage.value
    }
    if (txt2imgResultStorage.value.length > 0) {
        txt2img_imgs.value = txt2imgResultStorage.value
    }

    // 调用获取文生图采样方法的函数
    txt2ImgSampler()
    // 调用获取 ControlNet 的 Preprocessor 的函数
    controlNetPreProcessors()
    // 调用获取 ControlNet 的 Model 的函数
    controlNetModels()
})

</script>